namespace MoreLinq;

using System;
using System.Collections.Generic;

partial class MoreEnumerable
{
    /// <summary>
    /// Returns a single-element sequence containing the result of invoking the function.
    /// </summary>
    /// <remarks>
    /// This operator uses deferred execution and streams the results.
    /// If the resulting sequence is enumerated multiple times, the function will be
    /// invoked multiple times too.
    /// </remarks>
    /// <typeparam name="T">The type of the object returned by the function.</typeparam>
    /// <param name="function">The function to evaluate.</param>
    /// <returns>A sequence with the value resulting from invoking <paramref name="function"/>.</returns>

    public static IEnumerable<T> From<T>(Func<T> function)
    {
            return _(function);

            static IEnumerable<T> _(Func<T> function)
            {
#pragma warning disable CA1062 // Validate arguments of public methods
                yield return function();
#pragma warning restore CA1062 // Validate arguments of public methods
            }
        }

    /// <summary>
    /// Returns a sequence containing the result of invoking each parameter function in order.
    /// </summary>
    /// <remarks>
    /// This operator uses deferred execution and streams the results.
    /// If the resulting sequence is enumerated multiple times, the functions will be
    /// invoked multiple times too.
    /// </remarks>
    /// <typeparam name="T">The type of the object returned by the functions.</typeparam>
    /// <param name="function1">The first function to evaluate.</param>
    /// <param name="function2">The second function to evaluate.</param>
    /// <returns>A sequence with the values resulting from invoking <paramref name="function1"/> and <paramref name="function2"/>.</returns>

    public static IEnumerable<T> From<T>(Func<T> function1, Func<T> function2)
    {
            return _(function1, function2);

            static IEnumerable<T> _(Func<T> function1, Func<T> function2)
            {
#pragma warning disable CA1062 // Validate arguments of public methods
                yield return function1();
                yield return function2();
#pragma warning restore CA1062 // Validate arguments of public methods
            }
        }

    /// <summary>
    /// Returns a sequence containing the result of invoking each parameter function in order.
    /// </summary>
    /// <remarks>
    /// This operator uses deferred execution and streams the results.
    /// If the resulting sequence is enumerated multiple times, the functions will be
    /// invoked multiple times too.
    /// </remarks>
    /// <typeparam name="T">The type of the object returned by the functions.</typeparam>
    /// <param name="function1">The first function to evaluate.</param>
    /// <param name="function2">The second function to evaluate.</param>
    /// <param name="function3">The third function to evaluate.</param>
    /// <returns>A sequence with the values resulting from invoking <paramref name="function1"/>, <paramref name="function2"/> and <paramref name="function3"/>.</returns>

    public static IEnumerable<T> From<T>(Func<T> function1, Func<T> function2, Func<T> function3)
    {
            return _(function1, function2, function3);

            static IEnumerable<T> _(Func<T> function1, Func<T> function2, Func<T> function3)
            {
#pragma warning disable CA1062 // Validate arguments of public methods
                yield return function1();
                yield return function2();
                yield return function3();
#pragma warning restore CA1062 // Validate arguments of public methods
            }
        }

    /// <summary>
    /// Returns a sequence containing the values resulting from invoking (in order) each function in the source sequence of functions.
    /// </summary>
    /// <remarks>
    /// This operator uses deferred execution and streams the results.
    /// If the resulting sequence is enumerated multiple times, the functions will be
    /// invoked multiple times too.
    /// </remarks>
    /// <typeparam name="T">The type of the object returned by the functions.</typeparam>
    /// <param name="functions">The functions to evaluate.</param>
    /// <returns>A sequence with the values resulting from invoking all of the <paramref name="functions"/>.</returns>
    /// <exception cref="ArgumentNullException">When <paramref name="functions"/> is <c>null</c>.</exception>

    public static IEnumerable<T> From<T>(params Func<T>[] functions)
    {
            if (functions == null) throw new ArgumentNullException(nameof(functions));
            return Evaluate(functions);
        }
}
